import { isFunction } from '@wowon/utils';
import defaultContainer from './container';

const DEFAULT_CONFIG = {
  max: 10,
  defaultType: 'dialog',
  zIndexMin: 500,
  zIndexMax: 600,
  defaultProps: {},
};

const isFilterKey = (k) => k !== void 0 && k !== null;

class PopupSystem {
  constructor(options) {
    const { max, container, defaultType, defaultProps, zIndexMin, zIndexMax, onOpenOverflow, update } = Object.keys(
      options,
    ).reduce((s, name) => ((s[name] = options[name] !== void 0 ? options[name] : s[name]), s), DEFAULT_CONFIG);
    // 当前命名空间名称
    this.namespace = '';
    // 已经注册的内容列表
    this.registry = new Map();
    // 创建的弹出层以及内容
    this.popupList = new Map();
    // 渲染索引，每次弹窗聚焦时，都会递增1，超过设定的最大值时，所有弹窗从新排序，以防止和页面其他元素出现zIndex错位问题
    this.zIndexMin = zIndexMin;
    this.zIndexMax = zIndexMax;
    // 最大打开数量
    this.max = max;
    // 默认渲染的弹窗类型
    this.defaultType = defaultType;
    // 默认弹窗参数
    this.defaultProps = defaultProps;
    // 弹窗容器集合 { modal: Modal, drawer: Drawer, ... }
    this.container = Object.assign({}, defaultContainer, container);
    // open 超出最大打开数量回调
    this.onOpenOverflow = onOpenOverflow;
    // 强制更新组件
    this.update = update;
  }
  /** 获取打开索引 */
  getOpenKey(name, registerKey) {
    const isOpenKey = /^\w+\/\w+/.test(name);
    const keys = isOpenKey ? name.split('/') : [this.namespace, name].filter(isFilterKey);
    return (registerKey ? keys.slice(0, 2) : keys).join('/');
  }
  /** 设置当前的命名空间 */
  setNamespace(namespace) {
    this.namespace = namespace;
    this.update();
  }
  /** 注册弹出层配置 */
  register(items) {
    if (!items) return;
    for (let index = 0; index < items.length; index++) {
      const config = items[index];
      const registryKey = this.getOpenKey(config.name, true);
      if (this.registry.has(registryKey)) return;
      const item = {
        name: config.name,
        type: config.type || this.defaultType,
        content: config.content,
        // keep: config.keep,
        // preload: config.preload,
        // renderToCurrent: config.renderToCurrent,
        props: config.props,
        transfer: config.transfer,
        // events: config.events,
        // taskData: config.taskData,
        repeat: config.repeat,
        namespace: this.namespace,
        registryKey,
      };
      this.registry.set(registryKey, item);
      // if (item.preload) {
      //   this.create(item);
      //   if (item.props?.open) this.open(openKey);
      // }
    }
    this.update();
  }
  /** 注销配置 */
  unregister() {
    this.registry.forEach((item, registryKey) => {
      if (item.namespace === this.namespace && !item.keep) {
        this.registry.delete(registryKey);
      }
    });
    this.popupList.forEach((item, openKey) => {
      if (item.namespace === this.namespace && !item.keep) {
        this.popupList.delete(openKey);
      }
    });
    this.update();
  }
  /** 获取指定配置 */
  _getRegistry(name) {
    const registryKey = this.getOpenKey(name, true);
    if (this.registry.has(registryKey) === false) {
      console.error('No exist registry of ' + name + '!');
      return {};
    }
    return { openKey: this.getOpenKey(name), item: this.registry.get(registryKey) };
  }
  /** 修改打开的弹出层参数 */
  setPopupConfig(name, config) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const modalItem = this.popupList.get(openKey);
    const nextConfig = isFunction(config) ? config(modalItem) : { ...modalItem, ...config };
    this.popupList.set(openKey, nextConfig);
    this.update();
  }
  /** 判断弹出层是否已创建 */
  isCreated(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return false;
    return this.popupList.has(openKey);
  }
  /** 创建弹出层 */
  create(item) {
    const popupConfig = {
      registryKey: item.registryKey,
      openKey: item.openKey,
      namespace: item.namespace,
      name: item.name,
      type: item.type,
      content: item.content,
      zIndex: this.zIndexMin,
      open: false,
      focus: false,
      props: {
        ...this.defaultProps?.[item.type],
        ...item.props,
      },
      transfer: item.transfer,
      // ee: item.events ? eventEmitter() : null,
      // taskData: item.taskData,
      __readyClose: false,
      __registry: item,
    };

    const action = {
      focus: () => this.focus(item.openKey),
      open: () => this.open(item.openKey),
      hide: () => this.hide(item.openKey),
      close: () => this.close(item.openKey),
      destroy: () => this.destroy(item.openKey),
    };

    this.popupList.set(item.openKey, { ...popupConfig, action });
  }
  /** 删除弹出层 */
  remove(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey || !this.popupList.has(openKey)) return;
    this.popupList.delete(openKey);
  }
  /** 弹出层是否打开 */
  isOpen(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const popup = this.popupList.get(openKey);
    return popup?.open || false;
  }
  /** 修改打开的弹出层参数 */
  setProps(name, props) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const created = this.isCreated(openKey);
    if (!created) return;
    this.setPopupConfig(name, (config) => ({
      ...config,
      props: { ...config.props, ...props },
    }));
    this.update();
  }
  /** 打开弹出层 */
  open(name, props = {}) {
    if (this.popupList.size >= this.max) {
      this.onOpenOverflow?.();
      return;
    }
    let { openKey, item } = this._getRegistry(name);
    if (!openKey || !item) return;
    const created = this.isCreated(openKey);
    if (!created && item.repeat) {
      // 重复打开，需要对每个重复打开的弹窗配置不同的key，可以使用数据中的 key 作为不会重复的 key
      const repeatKey = item.repeat?.getKey?.(props) || props?.key;
      if (!isFilterKey(repeatKey)) {
        console.error('set config: "repeat: { getKey: (props) => uniqueKey }" when open same popup again.');
        return;
      }
      openKey = [name, repeatKey].join('/');
      if (!this.isCreated(openKey)) this.create({ ...item, openKey: this.getOpenKey(openKey) });
    } else if (!created && !item.repeat) {
      // 未创建并且不能重复打开
      this.create({ ...item, openKey });
    }
    // 设置弹窗的参数
    this.setPopupConfig(openKey, (config) => {
      return {
        ...config,
        open: true,
        props: { ...config.props, ...props },
      };
    });
    // 新打开的弹窗进行置顶
    this.focus(openKey);
    // console.log(this);
    this.update();
  }
  /** 隐藏弹出层 */
  hide(name, props) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const created = this.isCreated(openKey);
    if (created === false) return;
    this.setPopupConfig(openKey, (config) => ({
      ...config,
      open: false,
      focus: false,
      props: { ...config.props, ...props },
    }));
    this.update();
  }
  /** 关闭弹出层 */
  close(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const created = this.isCreated(openKey);
    if (!created) return;
    if (this.isOpen(openKey)) {
      // 更新弹窗内的关闭后回调方法
      this.setPopupConfig(openKey, { __readyClose: true });
      this.hide(openKey);
    } else {
      this.destroy(openKey);
    }
  }
  /** 切换隐藏和打开弹出层 */
  toggle(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    if (this.isOpen(openKey)) {
      this.hide(openKey);
    } else {
      this.open(openKey);
    }
  }
  /** 直接销毁弹出层 */
  destroy(name) {
    this.remove(name);
    this.update();
  }
  /** 置顶弹出层 */
  focus(name) {
    const openKey = this.getOpenKey(name);
    if (!openKey) return;
    const size = this.popupList.size;
    // this.popupList.forEach((item, popupKey) => {
    //   const focus = popupKey === openKey;
    //   if (item.focus !== focus) {
    //     const zIndex = focus ? size + 1 : size;
    //     this.setPopupConfig(popupKey, { focus, zIndex });
    //   }
    // });
    this.update();
  }
  // 处理关闭后的操作
  afterClose(name, callback) {
    callback?.();
    this.remove(name);
    this.update();
  }
  /** 获取已打开和隐藏的列表 */
  getCreatedList() {
    return Array.from(this.popupList).map(([openKey, item]) => ({
      namespace: item.namespace,
      name: item.name,
      open: item.open,
      focus: item.focus,
      zIndex: item.zIndex,
      props: item.props,
      // taskData: item.taskData,
      openKey,
    }));
  }
}

export default PopupSystem;
